#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#include <led.h>

static void ledTask( void *pvParameters );

enum LedModeEnum led_red_mode;
enum LedModeEnum led_green_mode;

u8 led_red_count;
u8 led_green_count;

xSemaphoreHandle led_xSemaphore;
xTaskHandle led_xHandle;

void LED_Configuration( void )
    {
    /* Enable LED GPIO clock */
    RCC_APB2PeriphClockCmd( LEDS_RCC_GPIO, ENABLE );
    
    GPIO_InitTypeDef GPIO_InitStructure;
    
    /* Configure LED pins as output push-pull */
    GPIO_InitStructure.GPIO_Pin   = LED_RED_PIN | LED_GREEN_PIN ;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init( LEDS_GPIO, &GPIO_InitStructure );
    
    GPIO_WriteBit(LEDS_GPIO, LED_RED_PIN, Bit_RESET);
    GPIO_WriteBit(LEDS_GPIO, LED_GREEN_PIN, Bit_RESET);
    
    led_green_mode = LED_OFF;
    led_red_mode = LED_OFF;
    
    /* create semaphore for led resource control */
    led_xSemaphore = xSemaphoreCreateMutex();
    
    /* create led task */
    xTaskCreate( ledTask, ( signed portCHAR * ) "Led",
                 configMINIMAL_STACK_SIZE, NULL, led_TASK_PRIORITY, &led_xHandle );
    
    /* turn led task to suspend mode */
    vTaskSuspend( led_xHandle );
    }

void LED_SetMode( enum LedPinEnum pin, enum LedModeEnum mode )
    {
    /* get led resource control */
    xSemaphoreTake( led_xSemaphore, portMAX_DELAY );
    
    if ( ( pin == LED_RED && mode == led_red_mode ) ||
       ( pin == LED_GREEN && mode == led_green_mode ) )
        {
        /* release led resource control */
        xSemaphoreGive( led_xSemaphore );
        return;
        }
    
    if ( pin == LED_RED )
        {
        led_red_mode = mode;
        
        if (mode == LED_ON)
            GPIO_WriteBit(LEDS_GPIO, LED_RED_PIN, Bit_SET);
        else
            GPIO_WriteBit(LEDS_GPIO, LED_RED_PIN, Bit_RESET);
        }
    else if ( pin == LED_GREEN )
        {
        led_green_mode = mode;
        
        if ( mode == LED_ON )
            GPIO_WriteBit( LEDS_GPIO, LED_GREEN_PIN, Bit_SET );
        else
            GPIO_WriteBit( LEDS_GPIO, LED_GREEN_PIN, Bit_RESET );
        }
    
    if ( mode == LED_BLINK || mode == LED_FLASH )
        vTaskResume( led_xHandle );
    else if ( ( led_red_mode != LED_BLINK || led_red_mode != LED_FLASH )
        && ( led_green_mode != LED_BLINK || led_green_mode != LED_FLASH ) )
        vTaskSuspend( led_xHandle );
    
    /* release led resource control */
    xSemaphoreGive( led_xSemaphore );
    }

static void ledTask( void *pvParameters )
    {
    const portTickType xDelay = 100 / portTICK_RATE_MS;
    
    while( 1 )
        {
        led_red_count++;
        led_green_count++;
        
        if ( led_red_mode == LED_BLINK )
            {
            if ( led_red_count >= 5)
                {
                led_red_count = 0;
                
                GPIO_WriteBit( LEDS_GPIO, LED_RED_PIN,
                              (BitAction)(1 - GPIO_ReadOutputDataBit(LEDS_GPIO, LED_RED_PIN)) );
                }
            }
        else if ( led_red_mode == LED_FLASH )
            {
            if ( led_red_count == 1 )
                GPIO_WriteBit( LEDS_GPIO, LED_RED_PIN, Bit_RESET );
            else if ( led_red_count >= 20 )
                {
                led_red_count = 0;
                GPIO_WriteBit( LEDS_GPIO, LED_RED_PIN, Bit_SET );
                }
            }
        
        if ( led_green_mode == LED_BLINK )
            {
                if ( led_green_count >= 5)
                {
                led_green_count = 0;
                
                GPIO_WriteBit( LEDS_GPIO, LED_GREEN_PIN,
                              (BitAction)(1 - GPIO_ReadOutputDataBit(LEDS_GPIO, LED_GREEN_PIN)) );
                }
            }
        else if ( led_green_mode == LED_FLASH )
            {
            if ( led_green_count == 1 )
                GPIO_WriteBit( LEDS_GPIO, LED_GREEN_PIN, Bit_RESET );
            else if ( led_green_count >= 20 )
                {
                led_green_count = 0;
                GPIO_WriteBit( LEDS_GPIO, LED_GREEN_PIN, Bit_SET );
                }
            }
    
        vTaskDelay(xDelay);
        }
    }